import amqplib = require('amqplib');
import logger from './logger';
import { config, get } from '../config';
import MsgNewDto from '../model/msgNewDto';
import Message from '../model/message';
import * as msgMapper from '../mapper/msgMapper';
import { instance as SocketProxy } from './socketioProxy';
import Resp from '../model/resp';
import { client as redis } from './redisProxy';

class RabbitmqProxy {
  _instance: RabbitmqProxy = null;
  conn: amqplib.Connection;
  channel: amqplib.Channel;
  static _instance: RabbitmqProxy;

  static async instance() {
    if (!this._instance) {
      let ins = new RabbitmqProxy();
      const conn = await amqplib.connect({
        username: `${config.rabbitmq.user}`,
        password: `${config.rabbitmq.password}`,
        hostname: `${config.rabbitmq.host}`,
        port: config.rabbitmq.port,
      });
      if(!this._instance){
        const io = SocketProxy.server;
        this._instance = ins;
        logger.info("Connected to RabbitMQ!");
        ins.conn = conn;
        const channel = await ins.conn.createChannel();
        ins.channel = channel;
        channel.assertQueue("post-msg");
        channel.consume('post-msg', async (message)=>{
          const msgdto: MsgNewDto = JSON.parse(message.content.toString());
          let msg = new Message(msgdto);
          const socketId: string = await redis.get(msg.receiver);
          msgMapper.ins(msg)
          .then(result=>{
              logger.info(`新建消息: ${JSON.stringify(msg)}`);
              msg.id = result[0] as any;
              if (socketId) {
                  msg.isPosted = 1;
                  io.to(socketId).emit("notice", msg);
                  logger.info(`创建消息成功，用户(${msg.receiver})在线，消息(${msg.id})成功发送`);
                  channel.ack(message);
                  msgMapper.upd_by_id(msg.id, {isPosted: 1});
              } else {
                  logger.info(`创建消息成功，用户(${msg.receiver})不在线，消息(${msg.id})稍后发送`);
                  channel.ack(message);
              }
          })
        })
      }
    }
    return this._instance;
  }
}

export async function init() {
  return RabbitmqProxy.instance();
}

export const instance = RabbitmqProxy.instance();

export default {
  init,
  /**
   * ### Notice that it is a promise, when imported anywhere else please await in async funtion or then-flow to initialize a var of its instance.
   * ```javascript
   * const { instance } = require('../utils/rabbitmqProxy');
   * 
   * app.get('/', async(req, res)=>{
   *  const rbmqProxy = await instance;
   *  const { channel: rbmq } = rbmqProxy;
   *  rbmq.sendToQueue("queue", "hello");
   * })
   * ```
   * ---
   * ### Or like this:
   * ```javascript
   * const RabbitmqProxy = require('../utils/rabbitmqProxy');
   * 
   * app.get('/', async(req, res)=>{
   *  const rbmqProxy = await RabbitmqProxy.instance;
   *  const { channel: rbmq } = rbmqProxy;
   *  rbmq.sendToQueue("queue", "hello");
   * })
   * ```
   * ---
   * ### Or like this:
   * ```javascript
   * const amqplib = require('amqplib');
   * const RabbitmqProxy = require('../utils/rabbitmqProxy');
   * 
   * //@type {amqplib.Channel}
   * let rbmq = null;
   * RabbitmqProxy.instance.then(rabbitmq=>{
   *   rbmq = rabbitmq.channel;
   * })
   * 
   * app.get('/', async(req, res)=>{
   *  rbmq.sendToQueue("queue", "hello");
   * })
   * ```
   */
  instance: RabbitmqProxy.instance()
}
